1   /*                        __    __  __  __    __  ___
2    *                       \  \  /  /    \  \  /  /  __/
3    *                        \  \/  /  /\  \  \/  /  /
4    *                         \____/__/  \__\____/__/.ɪᴏ
5    * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
6    */
7   package io.vavr.collection.euler;
8   
9   import io.vavr.Function1;
10  import io.vavr.collection.List;
11  import io.vavr.collection.Stream;
12  import org.assertj.core.api.Assertions;
13  import org.junit.Test;
14  
15  import static io.vavr.API.*;
16  import static org.assertj.core.api.Assertions.assertThat;
17  
18  /**
19   * <strong>Problem 23: Non-abundant sums</strong>
20   * <p>
21   * A perfect number is a number for which the sum of its proper divisors is
22   * exactly equal to the number. For example, the sum of the proper divisors of
23   * 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
24   * <p>
25   * A number n is called deficient if the sum of its proper divisors is less than
26   * n and it is called abundant if this sum exceeds n.
27   * <p>
28   * As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest
29   * number that can be written as the sum of two abundant numbers is 24. By
30   * mathematical analysis, it can be shown that all integers greater than 28123
31   * can be written as the sum of two abundant numbers. However, this upper limit
32   * cannot be reduced any further by analysis even though it is known that the
33   * greatest number that cannot be expressed as the sum of two abundant numbers
34   * is less than this limit.
35   * <p>
36   * Find the sum of all the positive integers which cannot be written as the sum
37   * of two abundant numbers.
38   * <p>
39   * See also <a href="https://projecteuler.net/problem=23">projecteuler.net
40   * problem 23</a>.
41   */
42  public class Euler23Test {
43  
44      private static final long SMALLEST_ABUNDANT_NUMBER = 12;
45      private static final long SMALLEST_NUMBER_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS = 2 * SMALLEST_ABUNDANT_NUMBER;
46      private static final long LOWER_LIMIT_FOUND_BY_MATHEMATICAL_ANALYSIS_FOR_NUMBERS_THAT_CAN_BE_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS = 28123;
47  
48      @Test
49      public void shouldSolveProblem23() {
50          List.range(1, SMALLEST_ABUNDANT_NUMBER).forEach(n -> Assertions.assertThat(isAbundant.apply(n)).isFalse());
51          Assertions.assertThat(isAbundant.apply(SMALLEST_ABUNDANT_NUMBER)).isTrue();
52          Assertions.assertThat(isAbundant.apply(28L)).isFalse();
53          assertThat(canBeWrittenAsTheSumOfTwoAbundantNumbers(SMALLEST_NUMBER_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS - 1)).isFalse();
54          assertThat(canBeWrittenAsTheSumOfTwoAbundantNumbers(SMALLEST_NUMBER_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS)).isTrue();
55          assertThat(canBeWrittenAsTheSumOfTwoAbundantNumbers(LOWER_LIMIT_FOUND_BY_MATHEMATICAL_ANALYSIS_FOR_NUMBERS_THAT_CAN_BE_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS + 1)).isTrue();
56          assertThat(sumOfAllPositiveIntegersThatCannotBeWrittenAsTheSumOfTwoAbundantNumbers()).isEqualTo(4179871L);
57      }
58  
59      private static long sumOfAllPositiveIntegersThatCannotBeWrittenAsTheSumOfTwoAbundantNumbers() {
60          return Stream.rangeClosed(1, LOWER_LIMIT_FOUND_BY_MATHEMATICAL_ANALYSIS_FOR_NUMBERS_THAT_CAN_BE_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS)
61                  .filter(l -> !canBeWrittenAsTheSumOfTwoAbundantNumbers(l))
62                  .sum().longValue();
63      }
64  
65      private static boolean canBeWrittenAsTheSumOfTwoAbundantNumbers(long l) {
66          return Match(l).of(
67                  Case($(n -> n < SMALLEST_NUMBER_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS), false),
68                  Case($(SMALLEST_NUMBER_WRITTEN_AS_THE_SUM_OF_TO_ABUNDANT_NUMBERS), true),
69                  Case($(), () -> Stream.rangeClosed(SMALLEST_ABUNDANT_NUMBER, l / 2).exists(a -> isAbundant.apply(a) && isAbundant.apply(l - a)))
70          );
71      }
72  
73      private static final Function1<Long, Boolean> isAbundant = Function1.of((Long l) -> Utils.divisors(l).sum().longValue() > l).memoized();
74  }